package com.dg.gaming.server;

import java.io.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.*;

import com.dg.gaming.api.common.*;
import com.dg.gaming.api.common.User;
import com.dg.gaming.api.common.msg.*;
import com.dg.gaming.api.common.playerstate.*;
import com.dg.gaming.api.common.util.*;
import com.dg.gaming.server.data.*;
import com.dg.gaming.server.data.mysql.*;
import javolution.util.*;
import nanoxml.*;
import org.xsocket.connection.*;
import com.dg.gaming.server.controllers.*;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2009</p>
 *
 * <p>Company: </p>
 *
 * @author not attributable
 * @version 1.0
 */
public class TableController {

    private TableUser userTable = null;
    private User user;
    private GameData gamedata;
    private NoMoveThread noMove;

    private ScheduledFuture moveSchedule = null;

    private static ScheduledThreadPoolExecutor shechuledExecutor = new
            ScheduledThreadPoolExecutor(10);

    private static Map<String,
                       TableUser> tables = Collections.
                                           synchronizedMap(new
            FastMap<String, TableUser>());

    private static Map<Long,
                       GameData> games = Collections.
                                         synchronizedMap(new
            FastMap<Long, GameData>());
    private static Map<Long,
                       DBUser> users = Collections.
                                       synchronizedMap(new
            FastMap<Long, DBUser>());


    private static GameData getGame(long gameID) throws
            Exception {
        GameData game = games.get(gameID);
        if (game == null) {
            game = new GameData(DBManager.getDBGame(gameID));
            if (game == null) {
                throw new Exception("Game with ID:" + String.valueOf(gameID) +
                                    " not found!!!");
            }
            games.put(gameID, game);
        }

        return game;
    }

    private static DBUser getUser(long userID) throws
            Exception {
        DBUser user = users.get(userID);
        if (user == null) {
            user = DBManager.getDBUser(userID);
            if (user == null) {
                throw new Exception("User with ID:" + String.valueOf(userID) +
                                    " not found!!!");
            }
            users.put(userID, user);
        }
        return user;
    }


    private static TableUser getTable(long userID, String tableNum, long gameID,
                                      TableController tblCon) throws
            Exception {
        TableUser tableObj = tables.get(tableNum);
        if (tableObj == null) {
            GameData gameData = getGame(gameID);
            tblCon.gamedata = gameData;
            DBGame dbGame = gameData.getGame();
            Game game = new Game(String.valueOf(dbGame.getGame_id()),
                                 dbGame.getMin_players(), dbGame.getMax_players());
            DBCurrentTable dbCurrTable = DBManager.getDBCurrentTable(Long.
                    parseLong(tableNum));
            if (dbCurrTable == null) {
                throw new Exception("Table with ID:" + String.valueOf(userID) +
                                    " not found!!!");
            }
            String data = null;
            try {
                data = new String(dbCurrTable.getMODELDATA(), "UTF8");
            } catch (Exception ex) {
                data = "";
            }
            if (data.length() > 0) {
                XMLElement message = new XMLElement();
                message.parseString(data);
                tableObj = new TableUser(new Table(message), dbCurrTable,
                                         gameData.getController(tblCon));

                for (Object elem : message.getChildren()) {
                    XMLElement child = (XMLElement) elem;
                    if (child.getName().equals(Msg.MODEL)) {
                        tableObj.getTable().setModel(gameData.getGameModel(
                                child));
                        break;
                    }
                }
            } else {
                tableObj = new TableUser(new Table(tableNum,
                        new User("", userID, 0, 0, 0, 0, 0), true,
                        new DGPropertyHash()), dbCurrTable,
                                         gameData.getController(tblCon));
            }
            tableObj.getTable().setTableNumber(tableNum);
            tableObj.getTable().setGame(game);
            tables.put(tableNum, tableObj);

        }else{
            tblCon.gamedata=getGame(gameID);
        }
        UserObject uo = tableObj.getUserObject(userID);

        if (uo == null) {

            DBUser dbuser = getUser(userID);
            User usr = new User(dbuser.getUsername(), userID, 0, 0, 0, 0, 0);
            uo = new UserObject(usr, tblCon);
            tableObj.getTable().addPlayer(usr);
            tableObj.setUser(uo);
        }

        if (tblCon.user == null) {
            tblCon.user = tableObj.getUser(userID);
        }

        return tableObj;
    }


    /** Convenience link to the server. */
    protected GameServer server;

    private INonBlockingConnection uo = null;
    /**
     * Constructor of a ServerTable parser.
     */
    public TableController(INonBlockingConnection uo) {
        // Set up fields
        this.server = GameServer.getInstance();
        this.uo = uo;
    }

    public String getId() {
        return uo.getId();
    }

    /**
     * This implements parsing of a table message.
     *
     * @param message
     */
    public synchronized void parseTableMessage(
            XMLElement message,
            String tableNum) {
        // First, make sure that the given table number is valid.
        // (Doing this once here means that all of the routines below can assume
        //  that the table is valid.)


        // Retrieve the message type
        String messageType = message.getName();

        try {
            // Parse the message
            if (messageType.equals(Msg.CONNECT_TABLE)) {
                tableConnect(new MsgConnectTable(message), tableNum);
            } else if (userTable != null) {
                Table table = userTable.getTable();
                if (messageType.equals(Msg.CHAT_TABLE)) {
                    chatTable(new MsgChatTable(message), tableNum);
                } else if (messageType.equals(Msg.JOIN_TABLE)) {
                    joinTable(new MsgJoinTable(message), tableNum, table);
                } else if (messageType.equals(Msg.SIT_DOWN)) {
                    sitDown(new MsgSitDown(message), tableNum, table);
                } else if (messageType.equals(Msg.STAND_UP)) {
                    standUp(new MsgStandUp(message), tableNum, table);
                } else if (messageType.equals(Msg.READY_TO_START)) {
                    readyToStart(new MsgReadyToStart(message), tableNum,
                                 table);
                } else if (messageType.equals(Msg.NEXT_PLAYER)) {
                    nextPlayer(new MsgNextPlayer(message), tableNum,
                               table);
                } else if (messageType.equals(Msg.EXIT_TABLE)) {
                    exitTable(new MsgExitTable(message), tableNum);
                } else if (messageType.equals(Msg.OFFER_DRAW)) {
                    offerDraw(new MsgOfferDraw(message), tableNum, table);
                } else if (messageType.equals(Msg.GAME_OVER)) {
                    gameOver(new MsgGameOver(message), tableNum, table);
                } else if (messageType.equals(Msg.CONTROLLER_PROPERTY)) {
                    controllerProperty(new MsgControllerProperty(message),
                                       tableNum);
                } else if (messageType.equals(Msg.CONTROLLER_OBJECT)) {
                    controllerObject(new MsgControllerObject(message),
                                     tableNum);
                } else if (messageType.equals(Msg.TABLE_PROPERTY)) {
                    tableProperty(new MsgTableProperty(message), table);
                }
                // Delegate to the correct controller to do a additional processing

                ServerTableController controller = userTable.getController();
                if (controller != null) {
                    controller.parseTableMessage(this, message, tableNum);
                }

            }

        } catch (TransmissionException transEx) {
            transEx.printStackTrace();
        } catch (ServerDataException sdEx) {
            serverSend(new MsgError(IError.SERVER_DATA_ERROR,
                                    sdEx.getMessage()));
        }
    }


    private void tableConnect(
            MsgConnectTable tblConnect, String tableNum) throws
            TransmissionException {
        try {
            userTable = getTable(tblConnect.getUserid(), tblConnect.getTableNum(),
                                 tblConnect.getGameID(), this);
            Worker.addConnection(this);
            userTable.getTable().setFullTransmittable(true);
            send(userTable.getTable(), true);
            MsgJoinTable commJoinTable = new MsgJoinTable(user.getUserid(),
                    tableNum);
            transmitToTablePlayers(user.getUserid(), tableNum, commJoinTable);
        } catch (Exception ex) {
            throw new TransmissionException(ex.getMessage());
        }
    }


    /**
     * Transmit a message to everyone in the room except the person who sent it
     * in the first place.
     *
     * @param conn        Connection back to the client.
     * @param commMessage Communications message.
     * @param tableNum    Table number.
     * @throws TransmissionException
     */
    private void chatTable(
            MsgChatTable commMessage, String tableNum) throws
            TransmissionException {
        // Broadcast to all users in a room (in this case a table)
        commMessage.setUsername(getUserId());

        // Retrieve table and then player list from the table
        transmitToTablePlayers(getUserId(), tableNum, commMessage);
    }

    /**
     * A client is sending a property to the server.  Update all the other clients
     * at the the table with this property.
     *
     * @param conn          Connection back to the client.
     * @param commContProp  Communications message.
     * @param tableNum      Table number.
     * @throws TransmissionException
     */
    private void controllerProperty(
            MsgControllerProperty commContProp,
            String tableNum) throws
            TransmissionException {
        // Transmit to other players
        commContProp.setUsername(getUserId());
        transmitToTablePlayers(getUserId(), tableNum, commContProp);

        // And also inform server controller
        ServerTableController controller = userTable.getController();
        if (controller != null) {

            // Retrieve and check that model isn't null
            GameModel model = controller.getModel(tableNum);
            if (model != null) {

                // Depending on type of property delegate to the correct adapter method
                switch (commContProp.getStatus()) {
                case MsgControllerProperty.TYPE_STRING:
                    controller.receiveProperty(model, commContProp.getKey(),
                                               commContProp.getValue());
                    return;

                case MsgControllerProperty.TYPE_INT:
                    int value = Integer.parseInt(commContProp.getValue());
                    controller.receiveProperty(model, commContProp.getKey(),
                                               value);
                    return;

                case MsgControllerProperty.TYPE_INT_TWO:
                    StringTokenizer st = new StringTokenizer(commContProp.
                            getValue());
                    int value1 = Integer.parseInt(st.nextToken());
                    int value2 = Integer.parseInt(st.nextToken());
                    controller.receiveProperty(model, commContProp.getKey(),
                                               value1, value2);
                    return;
                }
            }
        }
    }

    /**
     * A client is sending an object to the server. Update all other clients
     * at a table with this property.
     *
     * @param conn          Connection back to the client.
     * @param commContObj   Communications message.
     * @param tableNum      Table number.
     * @throws TransmissionException
     */
    private void controllerObject(
            MsgControllerObject commContObj,
            String tableNum) throws TransmissionException {
        // Transmit to other players
        commContObj.setUsername(getUserId());
        transmitToTablePlayers(getUserId(), tableNum, commContObj);

        // And send to server controller
        ServerTableController controller = userTable.getController();
        if (controller != null) {

            // Retrieve and check that model isn't null
            GameModel model = controller.getModel(tableNum);
            if (model != null) {
                controller.receiveObject(model, commContObj.getData(), tableNum);
            }
        }
    }

    /**
     * Send a table property to everyone.
     *
     * @param conn
     * @param commTableProperty
     * @param table              The table.
     */
    private void tableProperty(
            MsgTableProperty commTableProperty, Table table) {
        // Update table with key / value
        String key = commTableProperty.getKey();
        String value = commTableProperty.getValue();
        table.addProperty(key, value);

        broadcast(commTableProperty);
        sendDataMessageToAdmin(commTableProperty);
    }

    /**
     * broadcast
     *
     * @param commTableProperty ITransmittable
     */
    private void broadcast(ITransmittable commTableProperty) {
    }

    /**
     * The new way of performing a game over is that a client MAY inform a server
     * that a game is over if the game over detection requires a lot of
     * processing e.g. detecting chess game overs.  This means the client tries
     * to detect if its game over after each move and the only time the server
     * needs to check is if the client says its game over.  This enables the
     * server to be more efficient.
     *
     * @param conn          Connection to client.
     * @param commGameOver  GameOver object.
     * @param tableNum      Table number.
     * @param table         The table.
     */
    private void gameOver(
            MsgGameOver commGameOver, String tableNum,
            Table table) {
        // Ensure that the player sending this message is actually playing the game.
        long msgPlayerName = getUserId();
        if (!table.isParticipatingPlayer(msgPlayerName)) {
            return;
        }

        // Inform server controller that the client says the game is over.
        // The server should perform its own check to ensure this is correct
        ServerTableController controller = userTable.getController();
        if (controller != null) {
            // Check status.  It should either be user / resign
            int status = commGameOver.getStatus();
            switch (status) {
            case IGameOver.USER_RESIGNS:
                controller.userResigns(this, tableNum);
                break;
            default:
                controller.gameOver(this, tableNum, status);
                break;
            }
        }
    }

    /**
     * Transmit offer draw communications object.
     *
     * @param conn           Connection back to the client.
     * @param commOfferDraw  Communications message.
     * @param tableNum       Table number.
     * @param table          The table.
     */
    private void offerDraw(
            MsgOfferDraw commOfferDraw, String tableNum,
            Table table) {
        // Ensure that the player sending this message is actually playing the game.
        long msgPlayerName = getUserId();
        Player player = table.getPlayerList().getPlayer(msgPlayerName);
        if ((player == null) ||
            (player.getSeatNum() == Player.NOT_SEATED)) {
            return;
        }

        // Parse the message
        int status = commOfferDraw.getStatus();
        // Message can be accept, decline or request
        if (status == MsgInvite.REQUEST) {
            if (table.hasOutstandingDrawOffer()) {
                // If this table has an outstanding draw offer, then take this
                // request for draw as an accept (and do the code below for accept.)
                status = MsgInvite.ACCEPT;
            } else {
                // This table doesn't have an outstanding draw offer, so start
                // one, with this player being the instigator
                int offerSerialNum = table.startDrawOffer(player);

                // Send the message to everyone else to ask them for response.
                commOfferDraw.setSerialNum(offerSerialNum);
                commOfferDraw.setUsername(msgPlayerName);
                transmitToTablePlayers(msgPlayerName, tableNum,
                                       commOfferDraw, true);
            }
        }

        if (status == MsgInvite.ACCEPT) {
            table.acceptDrawOffer(player, commOfferDraw.getSerialNum());

            if (table.drawOfferComplete()) {
                // Clear the draw offer from the table
                table.clearDrawOffer();

                // Delegate this call to the server controller
                ServerTableController controller = userTable.getController();
                if (controller != null) {
                    controller.usersAgreeDraw(this, tableNum);
                }
            }
        } else if (status == MsgInvite.DECLINE) {
            if (table.declineDrawOffer(player, commOfferDraw.getSerialNum())) {
                // Clear the draw offer from the table
                table.clearDrawOffer();

                // Tell everyone that this player declined the draw offer
                commOfferDraw.setUsername(msgPlayerName);
                transmitToTablePlayers(msgPlayerName, tableNum,
                                       commOfferDraw);
            }
        }
    }

    /**
     * Transmit a next player request to users.
     *
     * @param conn            Connection to client.
     * @param commNextPlayer  Next player message.
     * @param tableNum        Table number.
     * @param table           The table.
     */

    public void sendTimerMessage(boolean loose) {
        try {
            cancelAllPlayerNoMove();
            if (loose) {
                MsgNoMove nomove = new MsgNoMove(0, true);
                send(nomove, true);
            } else {
                int losein = gamedata.getGame().
                             getLoseaftersecond();
                MsgNoMove nomove = new MsgNoMove(losein, true);
                send(nomove, true);
                noMove = new NoMoveThread(this, true);
                moveSchedule = shechuledExecutor.schedule(noMove,
                        losein,
                        TimeUnit.SECONDS);

            }
        } catch (Exception ex) {

        }
    }


    private void cancelAllPlayerNoMove() {
        for (UserObject elem : userTable.getUsers().values()) {
            TableController contr = elem.getNbc();
            if (contr.moveSchedule != null) {
                contr.moveSchedule.cancel(true);
            }
            if (contr.noMove != null) {
                contr.noMove.cancel();
            }
            contr.moveSchedule = null;
            contr.noMove = null;
        }
    }

    private void nextPlayer(
            MsgNextPlayer commNextPlayer, String tableNum,
            Table table) {
        cancelAllPlayerNoMove();
        // Advance to the next player
        table.nextPlayer();
        XMLElement chiled = commNextPlayer.getChiled();
        if (chiled != null) {
            chiled.setAttribute(MsgTableMessage.XML_ATT_TABLE_NUM, tableNum);
            parseTableMessage(chiled, tableNum);
        }
        // Set the username of the next player and transmit this to players at table
        commNextPlayer.setUsername(table.getPlayerList().getCurrentPlayerId());
        transmitToTablePlayers(tableNum, commNextPlayer);
        table = userTable.getTable();
        try {
            table.setFullTransmittable(true);
            byte[] bytes = table.flatten().toString().getBytes(
                    "UTF8");
            userTable.getDbCurrTable().setMODELDATA(bytes);
            DBManager.saveDBCurrentTable(userTable.getDbCurrTable());
            userTable.getDbCurrTable().setMODELDATA(null);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        sendDataMessageToAdmin(commNextPlayer);
        long currentPlayer = table.getPlayerList().getCurrentPlayerId();
        TableController tblContr = userTable.getUserObject(currentPlayer).
                                   getNbc();
        if (tblContr != null) {
            tblContr.noMove = new NoMoveThread(tblContr, false);
            tblContr.moveSchedule = shechuledExecutor.schedule(tblContr.noMove,
                    gamedata.getGame().
                    getDeleyinsec(),
                    TimeUnit.SECONDS);

        }

    }

    /**
     * Transmit exit table message.,
     *
     * @param conn           Connection to client.
     * @param commExitTable  Exit table communication message.
     * @param tableNum       Table number.
     */
    private void exitTable(
            MsgExitTable commExitTable, String tableNum) throws
            ServerDataException {
        // Retrieve table list
//        TableList tableList = getTableList();
//        long username = getUserId();
//        int numOfTables = tableList.size();
//
//        // Make sure the player was removed before sending out messages
//        if (tableList.removePlayer(tableNum, username)) {
//
//            // Broadcast to all user and admin
//            MsgExitTable commExitReply = new MsgExitTable(tableNum, username);
//            broadcast(username, commExitReply); // inform all users
//            sendDataMessageToAdmin(commExitReply);
//
//            // Update snapshot as a table may have been removed
//            if (tableList.size() != numOfTables) {
//                server.getServerData().updateSnapshot(
//                        getGameID(),
//                        getUserList().size(),
//                        getTableList().size());
//            }
//        }
    }

    /**
     * User joins a table.
     *
     * @param conn            Connection to client.
     * @param commJoinTable   CommJoinTable communication message from client.
     * @param tableNum        Table number we are interested in.
     * @param table           The table we are interested in.
     */
    private void joinTable(
            MsgJoinTable commJoinTable, String tableNum,
            Table table) {
//        // Unpack join table message from XML
//        long username = getUserId();
//        User newUser = getUserList().getUser(username);
//
//        // Make sure the player joined successfully before sending out replies
//        if (table.addPlayer(newUser)) {
//
//            // Create response join table message
//            // The user who is joining also gets the player list
//            commJoinTable = new MsgJoinTable(username, table);
//            transmit(username, commJoinTable);
//
//            // set back to normal again and broadcast the table object to all users
//            // omiting this user (as they already have it) and to admin.
//            commJoinTable = new MsgJoinTable(username, tableNum);
//            broadcast(username, commJoinTable);
//            sendDataMessageToAdmin(commJoinTable);
//        }
    }

    /**
     * User stands up at a table.
     *
     * @param conn         Connection to the server.
     * @param commStandUp  CommStandUp message.
     * @param tableNum     The table number we're standing up from.
     * @param table        The table we're standing up from.
     */
    private void standUp(MsgStandUp commStandUp,
                         String tableNum, Table table) {
        // Get the player
        Player player = table.getPlayerList().getPlayer(getUserId());
        if (player == null) {
            return;
        }

        // Change the player's state
        player.stand();

        // Transmit to all players at table & admin that this user has sat down
        MsgPlayerState commPlayerState = new MsgPlayerState(
                tableNum, getUserId(), player.getState());
        transmitToTablePlayers(tableNum, commPlayerState);
        sendDataMessageToAdmin(commPlayerState);
    }

    /**
     * Ready to start.
     *
     * @param conn         Connection to client.
     * @param commReady    Ready to start communications message.
     * @param tableNum     Table number.
     * @param table        The table
     */
    private void readyToStart(
            MsgReadyToStart commReady, String tableNum,
            Table table) {
        // Get the player
        PlayerList players = table.getPlayerList();
        Player player = players.getPlayer(getUserId());
        if (player == null) {
            return;
        }

        // Change the player's state
        player.start();

        // Transmit state to table players & admin
        MsgPlayerState commPlayerState = new MsgPlayerState(
                tableNum, getUserId(), player.getState());
        transmitToTablePlayers(tableNum, commPlayerState);
        sendDataMessageToAdmin(commPlayerState);

        // Check if this game is to start
        int numOfPlayers = table.getNumOfPlayers();
        int count = players.getPlayerStateCount(PlayerState.READY_TO_START);

        if (count == numOfPlayers) {

            // Update all the users to have the status of started and
            // inform everyone at the table
            Vector playersVector = players.getPlayersSortedBySeat(PlayerState.
                    READY_TO_START);
            for (int i = 0; i < playersVector.size(); i++) {
                Player curPlayer = (Player) playersVector.get(i);
                curPlayer.start();

                // Transmit "start" state to table players
                commPlayerState = new MsgPlayerState(
                        tableNum, curPlayer.getPlayerId(), curPlayer.getState());
                transmitToTablePlayer(curPlayer.getPlayerId(), tableNum,
                                      commPlayerState);

                sendDataMessageToAdmin(commPlayerState);
            }

            // Reset the current player
            players.resetCurrentPlayer();

            // Inform the server controller if it exists
            ServerTableController controller = userTable.getController();
            if (controller != null) {
                controller.startGame(tableNum);
            }

            // Send individual game start messages to all players
            MsgStartGame commStartGame = new MsgStartGame(players.
                    getCurrentPlayerId(), tableNum);
            transmitToTablePlayers(tableNum, commStartGame);

            // Let the game specific server send initial messages to the clients
            // now that they've been told the game has started.
            if (controller != null) {
                controller.sendInitialClientMessages(this, tableNum);
            }
        }
    }

    /**
     * sendDataMessageToAdmin
     *
     * @param commPlayerState MsgPlayerState
     */
    public void sendDataMessageToAdmin(ITransmittable message) {
    }

    /**
     * User sits down at a table.
     *
     * @param conn         Connection to client.
     * @param commSitDown  Message to sit down.
     * @param tableNum     Table number.
     * @param table        The table we're sitting at
     */
    private void sitDown(MsgSitDown commSitDown,
                         String tableNum, Table table) {
        // Get the Player
        PlayerList players = table.getPlayerList();
        Player player = players.getPlayer(getUserId());
        if (player == null) {
            return;
        }

        int seatNum = commSitDown.getSeatNum();

        // Ensure that the seat is free
        if (players.isSeatFree(seatNum)) {
            player.setSeatNum(seatNum);
            player.sit();

            // Transmit to all users that this user has sat down
            MsgPlayerState commPlayerState = new MsgPlayerState(
                    tableNum, getUserId(), seatNum);
            transmitToTablePlayers(tableNum, commPlayerState);
            sendDataMessageToAdmin(commPlayerState);
        }
    }

    public void transmitToTablePlayer(long usernameTo, String tableNum,
                                      ITransmittable transObject) {
        if (transObject instanceof MsgTableMessage) {
            ((MsgTableMessage) transObject).setTableNum(tableNum);
        }

        // Insure that that user is actually in this table
        Table table = userTable.getTable();
        if (table != null) {
            PlayerList playerList = table.getPlayerList();
            Player player = playerList.getPlayer(usernameTo);
            if (player != null) {
                transmit(usernameTo, transObject);
            }
        }
    }

    /**
     * Transmit a message to the specified table.
     *
     * @param tableNum
     * @param transObject
     */
    public void transmitToTablePlayers(String tableNum,
                                       ITransmittable transObject) {
        if (transObject instanceof MsgTableMessage) {
            ((MsgTableMessage) transObject).setTableNum(userTable.getTable().
                    getTableNum());
        }

        Table table = userTable.getTable();
        if (table != null) {
            Vector players = table.getPlayerList().getPlayers();

            for (int i = 0; i < players.size(); i++) {
                Player player = (Player) players.get(i);
                long username = player.getPlayerId();
                transmit(username, transObject);
            }
        }
    }

    /**
     * Transmit a messgae to a table but omit a user (this user will generally
     * be the player who has created the message in the first place).
     *
     * @param omitUser     Username to omit from the list (usually the sender).
     * @param tableNum     Table number to transmit message to.
     * @param transObject  Object which implements the ITransmittable interface.
     */
    public void transmitToTablePlayers(long omitUser, String tableNum,
                                       ITransmittable transObject) {
        transmitToTablePlayers(omitUser, tableNum, transObject, false);
    }

    /**
     * Overloaded version which only transmits to players in a game in progress i.e. transmits
     * to players whose state is "PlayerStateGameStarted".
     *
     * @param omitUser       Username to omit from the list (usually the sender).
     * @param tableNum       Table number to transmit message to.
     * @param transObject    Object which implements the ITransmittable interface.
     * @param gameInProgress If true only transmits to players if they are playing.
     */
    public void transmitToTablePlayers(long omitUser, String tableNum,
                                       ITransmittable transObject,
                                       boolean gameInProgress) {
        if (transObject instanceof MsgTableMessage) {
            ((MsgTableMessage) transObject).setTableNum(tableNum);
        }

        Table table = userTable.getTable();
        if (table != null) {
            Vector players = table.getPlayerList().getPlayers();

            // Loop throught the various players and transmit the message
            for (int i = 0; i < players.size(); i++) {
                Player player = (Player) players.get(i);
                long username = player.getPlayerId();

                if (username != omitUser) {
                    if (gameInProgress) {
                        if (player.getState() instanceof PlayerStateGameStarted) {
                            transmit(getUserId(), transObject);
                        }
                    } else {
                        transmit(username, transObject);
                    }
                }
            }
        }
    }

    /**
     * transmit
     *
     * @param userID long
     * @param transObject ITransmittable
     */
    private void transmit(long userID, ITransmittable transObject) {
        userTable.getUserObject(userID).getNbc().serverSend(transObject);
    }

    /**
     * getUserId
     *
     * @return long
     */
    public long getUserId() {
        return user.getUserid();
    }

    public void serverSend(ITransmittable transObject) {
        send(transObject, true);
    }

    /**
     * send
     *
     * @param transObject ITransmittable
     * @param sendtoadmin boolean
     */
    private void send(ITransmittable transObject, boolean sendtoadmin) {
        // Retrieve XMLElement from the object and flatten to a String.
        String message = transObject.flatten().toString();

        // Send down the socket to the receiving end
        try {
            long userID = this.getUserId();
            uo.write((message + "\n").getBytes("UTF8"));
            uo.flush();
        } catch (BufferOverflowException ex) {
        } catch (IOException ex) {
        }

    }

    public TableUser getUserTable() {
        return userTable;
    }
}
